package svnloc;

import java.io.*;
import java.util.*;

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.cli.*;
import org.tmatesoft.svn.cli.svn.SVNCommandEnvironment;

public class svnloc extends org.tmatesoft.svn.cli.svn.SVN {

  private String default_fileExt = ".c;.cpp;.cc;.cxx;.h;.hpp;.hxx;.inl;.y;.l;.py;.java;.pjava;.s;.g;.ftl;.js;.css;.xml;.properties;";

  private String excludeFile, outPut;
  private BufferedWriter outPutWriter;
  private int startVer, endVer;
  
  private HashSet<String> excludeSet;
  private HashSet<String> fileExtSet;
  private ArrayList<String> excludeString;

  public static void main(String[] args) 
  {
    svnloc worker = new svnloc();
    worker.registerCommands();
    worker.registerOptions();
    worker.run_svn(args);
  };
  
  private void run_svn(String[] args) 
  {
    if (!getValueFromArgs(args)) 
    {
      printBasicUsage();
      System.exit(-1);
    }

    getExcludeList();

    /* init options */
    String[] options = new String[7];
    options[0] = "diff";
    options[1] = "--config-option";
    /* save password, otherwise password will be input several times */
    options[2] = "servers:global:store-passwords=yes";
    options[3] = "--config-option";
    options[4] = "servers:global:store-plaintext-passwords=yes";
    options[5] = "-c";

    initSvnloc();

    SVNCommandLine commandLine = new SVNCommandLine(true);

    if (endVer == -1) 
    {
      endVer = getCurrentSvnVersion(commandLine);
    }
    
    if (endVer == -1) 
    {
      System.err.println("cannot get the last version!");
      System.exit(-1);
    }

    diffAnalyzer analyzer = new diffAnalyzer(excludeSet,excludeString,fileExtSet);

    for (int i = startVer; i <= endVer; i++) 
    {
      options[6] = String.valueOf(i);
      ArrayList<String> resultInfo;
      String result;

      resultInfo = executeCommand(commandLine, options);
      
      if (resultInfo != null)
      {
        result = analyzer.work(i,resultInfo);
      }
      else
      {
        result = null;
      }

      if (result == null) 
      {
        result = String.format("%10d,%10d,%10d,%10d,%10d\r\n", i, -1,-1, -1, -1);
      }
      
      write(result);
    }
    
    write(String.format("%10d,%10d,%10d,%10d,%10d\r\n", 0, analyzer.add, analyzer.del, analyzer.mod, analyzer.total));
  }

  /* inherit from super */
  protected String getProgramName() 
  {
    return "svnloc";
  }

  private void write(String text) 
  {
    try 
    {
      
      if (outPutWriter == null) 
      {
        outPutWriter = new BufferedWriter(new FileWriter(new File(outPut)));
        
        outPutWriter.write("   version      added    deleted   modified      total\r\n");
      }
      
      outPutWriter.write(text);
      outPutWriter.flush();
    }
    catch (IOException e) 
    {
      e.printStackTrace();
      System.err.println("Cannot write result to file");
      System.exit(-1);
    }
  }

  private ArrayList<String> executeCommand(SVNCommandLine commandLine, String[] options) 
  {
    ByteArrayOutputStream commandOut = new ByteArrayOutputStream();

    PrintStream svnDiffOut = new PrintStream(commandOut);

    AbstractSVNCommandEnvironment env = createCommandEnvironment(svnDiffOut);
  
    try 
    {
      commandLine.init(options);
    }
    catch (SVNException e) 
    {
      System.err.println(e.getMessage());
      return null;
    }

    try 
    {
      env.init(commandLine);
      env.initClientManager();
    }
    catch (SVNException e) 
    {
      System.err.println(e.getMessage());
      env.dispose();
      return null;
    }

    if (!env.run()) 
    {
      env.dispose();
      return null;
    }
    
    env.dispose();
    
    ByteArrayInputStream in = new ByteArrayInputStream(commandOut.toByteArray());
    
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    
    
    String line;

    ArrayList<String> resultInfo = new ArrayList<String>();
    
    try 
    {
      while ((line = reader.readLine()) != null) 
      { 
        resultInfo.add(line); 
      }
    }
    catch (IOException e) 
    {
      e.printStackTrace();
      return null;
    }

    return resultInfo;
  }

  private void getExcludeList() 
  {
    String fileExt = default_fileExt;
    
    try 
    {
      
      String s;
      
      BufferedReader file = new BufferedReader(new FileReader(excludeFile));
      
      while ((s = file.readLine()) != null) 
      {
        if (s.startsWith("--file:"))
        {
          fileExt = s.substring(7).toLowerCase();
        }
        else if (s.endsWith("*")) 
        {
          /* should have only one "*" at the end */
          excludeString.add(s.substring(0, s.length() - 1).toLowerCase() );
        }
        else 
        {
          excludeSet.add(s.trim().toLowerCase());
        }
      }
    }
    catch (FileNotFoundException e1) 
    {
      e1.printStackTrace();
    }
    catch (IOException e) 
    {
      e.printStackTrace();
    }
    
    String[] fileExtList = fileExt.split(";");
    
    for (int i = 0; i<fileExtList.length; i++)
    {
      fileExtSet.add ( fileExtList[i].trim() );
    }

    return;
  }

  static private void initSvnloc() 
  {
    DAVRepositoryFactory.setup();
    SVNRepositoryFactoryImpl.setup();
    FSRepositoryFactory.setup();
  }

  public svnloc() 
  {
    excludeSet = new HashSet<String>();
    excludeString = new ArrayList<String>();
    fileExtSet = new HashSet<String>();
    
    outPut = "result.txt";
    outPutWriter = null;
  }

  // inherit from supper
  protected void printBasicUsage() 
  {
    PrintStream out = System.err;
  
    out.println(getProgramName() + " startVersion endVersion [excludeFile]");
    out.println("\n# means the current version");
    out.println("result file(result.txt) located at current path");
    out.println("exclude File support *");
    out.println("\nexample:");
    out.println("    " + getProgramName() + " 200 #");
    out.println("And in Linux, # should in quotes, like this: ");
                out.println("    " + getProgramName() + " 200 \"#\"");
  }

  private int getCurrentSvnVersion(SVNCommandLine commandLine) 
  {
    String line;
    String options[] = new String[1];
    ArrayList<String> resultInfo;
    
    options[0] = "info";
    
    resultInfo = executeCommand(commandLine, options);
    
    if ( resultInfo == null)
    {
      return -1;
    }

    for (int i = 0; i < resultInfo.size(); i++) 
    {
      line = resultInfo.get(i);
    
      if (line.startsWith("Revision: ")) 
      {
        return Integer.parseInt(line.substring(10));
      }
    }

    return -1;
  }

  private boolean getValueFromArgs(String[] args) 
  {
    if (args.length < 2 || args.length > 3) 
    {
      return false;
    }
    
    startVer = Integer.parseInt(args[0]);
    
    if (args[1].equals("#") || args[1].equals("\"#\"") ) 
    {
      endVer = -1;
    } 
    else 
    {
      endVer = Integer.parseInt(args[1]);
    }
    
    if (args.length == 3) 
    {
      excludeFile = args[2];
    }
    else
    {
      excludeFile = "exclude.txt";
    }

    return true;
  }

  protected AbstractSVNCommandEnvironment createCommandEnvironment(PrintStream out) 
  {
    return new SVNCommandEnvironment(getProgramName(), out, System.err,System.in);
  }
}
